package com.mo.config;

import com.mo.config.filter.TokenVerifyFilter;
import com.mo.config.handler.MyAccessDeniedHandler;
import com.mo.config.handler.MyAuthenticationFailureHandler;
import com.mo.config.handler.MyAuthenticationSuccessHandler;
import com.mo.config.handler.MyLogoutSuccessHandler;
import com.mo.constant.Constants;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;


import java.util.Arrays;

/**
 * @author mo
 * version
 */
@Configuration
@EnableMethodSecurity //开启注解的方式设置权限
public class SecurityConfig {
    @Resource
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Resource
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Resource
    private TokenVerifyFilter tokenVerifyFilter;
    @Resource
    private MyLogoutSuccessHandler myLogoutSuccessHandler;

    @Resource
    private MyAccessDeniedHandler myAccessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, CorsConfigurationSource configurationSource) throws Exception {
        return httpSecurity.formLogin((formLogin) -> {
            //api/login是设置向后端提交账户密码的接口
            formLogin.loginProcessingUrl(Constants.LOGIN_URI) //登录处理地址，不需要写Controller
                    //这边是前端地参数名，后端的账号密码框架会调用UserDetails里的getPassword和 getUsername来获取
                    .usernameParameter("loginAct")
                    .passwordParameter("loginPwd")
                    .successHandler(myAuthenticationSuccessHandler)
                    .failureHandler(myAuthenticationFailureHandler);
        })
                .authorizeHttpRequests((authorize) ->
                {
                    authorize.requestMatchers(Constants.LOGIN_URI).permitAll().anyRequest().authenticated();
                })
                .sessionManagement((session)->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .csrf(AbstractHttpConfigurer::disable)//方法引用，禁用跨站请求伪造
                //支持跨域请求
                .cors((cors) -> cors.configurationSource(configurationSource))
                //就是把自定义的token验证过滤器加入到LogoutFilter过滤器前面
                .addFilterBefore(tokenVerifyFilter, LogoutFilter.class)
                //退出登录
                .logout((logout)->
                        logout.logoutUrl("/api/logout").logoutSuccessHandler(myLogoutSuccessHandler))
                //无权限时触发这里
                .exceptionHandling((exceptionHandling)->{
                    exceptionHandling.accessDeniedHandler(myAccessDeniedHandler);
                })
                .build();
    }

    @Bean
    public CorsConfigurationSource configurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*")); //允许任何来源，http://localhost:8081
        configuration.setAllowedMethods(Arrays.asList("*")); //允许任何请求方法，post、get、put、delete
        configuration.setAllowedHeaders(Arrays.asList("*")); //允许任何的请求头
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        //请求后端的不管什么路径，都采用上面三种策略，**表多层路径
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
